package com.haiqiu.tools.captcha.utils;

import com.haiqiu.tools.captcha.entity.Captcha;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Base64;
import java.util.Random;
import java.util.UUID;

/**
 * @author HaiQiu
 * @since 2023/1/29
 */
public class ImgVerifyCode {

    private static int weight = 100;           //验证码图片的长和宽
    private static int height = 40;
    private static String text;                //用来保存验证码的文本内容
    private static Random r = new Random();    //获取随机数对象


    //private String[] fontNames = {"宋体", "华文楷体", "黑体", "微软雅黑", "楷体_GB2312"};   //字体数组
    //字体数组
    private static String[] fontNames = {"Georgia"};
    //验证码数组
    private static String codes = "23456789abcdefghjkmnopqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ";

    private static final Logger logger = LoggerFactory.getLogger(ImgVerifyCode.class);


    /**
     * 生成验证码
     * @return 验证码
     */
    public static Captcha generateCode() {
        return generateCode(4);
    }

    public static Captcha generateCode(int length) {
        BufferedImage image = getImage(length);
        //3.输出图片
        Captcha captcha = new Captcha();
        captcha.setBufferedImage(image);
        String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
        captcha.setUuid(uuid);
        captcha.setBase64(toBase64FromImage(image));
        captcha.setCode(text);
        return captcha;
    }


    /**
     * 直接将验证码返回到浏览器
     *
     * @param response
     */
    public static void writeCodeToResponse(BufferedImage codeImg, HttpServletResponse response) {
        try {
            // 设置输出流
            response.setContentType("image/jpeg");
            response.setHeader("Pragma", "No-cache");
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expire", 0);
            // 写入输出流中
            ImageIO.write(codeImg, "JPEG", response.getOutputStream());
        } catch (FileNotFoundException e) {
            logger.error("文件未发现异常", e);
        } catch (IOException e) {
            logger.error("写验证码异常", e);
        }
    }

    /**
     * BufferedImage 编码转换为 base64
     *
     * @param img
     * @return
     */
    public static String toBase64FromImage(BufferedImage img) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        try {
            // 设置图片的格式
            ImageIO.write(img, "jpg", stream);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        byte[] bytes = Base64.getEncoder().encode(stream.toByteArray());
        String base64 = new String(bytes);
        return "data:image/jpeg;base64," + base64;
    }

    /**
     * base64 编码转换为 BufferedImage
     *
     * @param base64
     * @return
     */
    private static BufferedImage base64ToBufferedImage(String base64) {
        try {
            byte[] bytes1 = Base64.getEncoder().encode(base64.getBytes());
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes1);
            return ImageIO.read(bais);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }



    /**
     * 获取随机的颜色
     *
     * @return
     */
    private static Color randomColor() {
        int r = ImgVerifyCode.r.nextInt(225);  //这里为什么是225，因为当r，g，b都为255时，即为白色，为了好辨认，需要颜色深一点。
        int g = ImgVerifyCode.r.nextInt(225);
        int b = ImgVerifyCode.r.nextInt(225);
        return new Color(r, g, b);            //返回一个随机颜色
    }

    /**
     * 获取随机字体
     *
     * @return
     */
    private static Font randomFont() {
        int index = r.nextInt(fontNames.length);  //获取随机的字体
        String fontName = fontNames[index];
        int style = r.nextInt(4);         //随机获取字体的样式，0是无样式，1是加粗，2是斜体，3是加粗加斜体
        int size = r.nextInt(10) + 24;    //随机获取字体的大小
        return new Font(fontName, style, size);   //返回一个随机的字体
    }

    /**
     * 获取随机字符
     *
     * @return
     */
    private static char randomChar() {
        int index = r.nextInt(codes.length());
        return codes.charAt(index);
    }

    /**
     * 画干扰线，验证码干扰线用来防止计算机解析图片
     *
     * @param image
     */
    private static void drawLine(BufferedImage image) {
        int num = r.nextInt(10); //定义干扰线的数量
        Graphics2D g = (Graphics2D) image.getGraphics();
        for (int i = 0; i < num; i++) {
            int x1 = r.nextInt(weight);
            int y1 = r.nextInt(height);
            int x2 = r.nextInt(weight);
            int y2 = r.nextInt(height);
            g.setColor(randomColor());
            g.drawLine(x1, y1, x2, y2);
        }
    }

    /**
     * 创建图片的方法
     *
     * @return
     */
    private static BufferedImage createImage() {
        //创建图片缓冲区
        BufferedImage image = new BufferedImage(weight, height, BufferedImage.TYPE_INT_RGB);
        //获取画笔
        Graphics2D g = (Graphics2D) image.getGraphics();
        //设置背景色随机
//        g.setColor(new Color(255, 255, r.nextInt(245) + 10));
        g.setColor(Color.white);
        g.fillRect(0, 0, weight, height);
        //返回一个图片
        return image;
    }
    private static BufferedImage createImage(int count) {
        ImgVerifyCode.weight = count * 25;
        //创建图片缓冲区
        BufferedImage image = new BufferedImage(weight, height, BufferedImage.TYPE_INT_RGB);
        //获取画笔
        Graphics2D g = (Graphics2D) image.getGraphics();
        //设置背景色随机
//        g.setColor(new Color(255, 255, r.nextInt(245) + 10));
        g.setColor(Color.white);
        g.fillRect(0, 0, weight, height);
        //返回一个图片
        return image;
    }

    /**
     * 获取验证码图片的方法
     *
     * @return
     */
    public static BufferedImage getImage() {
        return getImage(4);
    }


    /**
     * 获取指定长度得验证码
     * @param length 指定长度
     * @return
     */
    public static BufferedImage getImage(int length) {
        BufferedImage image = createImage();
        Graphics2D g = (Graphics2D) image.getGraphics(); //获取画笔
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++)             //画四个字符即可
        {
            String s = randomChar() + "";      //随机生成字符，因为只有画字符串的方法，没有画字符的方法，所以需要将字符变成字符串再画
            sb.append(s);                      //添加到StringBuilder里面
            float x = i * 1.0F * weight / 4;   //定义字符的x坐标
            g.setFont(randomFont());           //设置字体，随机
            g.setColor(randomColor());         //设置颜色，随机
            g.drawString(s, x, height - 5);
        }
        ImgVerifyCode.text = sb.toString();
        drawLine(image);
        return image;
    }
}
